import java.util.*;
import java.math.*;
import org.jscience.mathematics.vector.*;
import org.jscience.mathematics.number.*;
import org.lsmp.djep.groupJep.values.Polynomial;
import org.lsmp.djep.groupJep.groups.Zn;

public class TestController implements Controller{

    static HashMap<Integer, Player> global_players;
    static HashMap<Integer, User> users = new HashMap<Integer, User>();
    static CircuitController cc;

    public static void main(String[] args){
        //Doing some math as to how many triples needs generating. 
        Utilities.update();

        /**
         * Testing matrices using rings
         */
        System.out.println("========== Testing matrices using rings: ==========");
        ModuloInteger.setModulus(new LargeInteger(Utilities.F));

        DenseMatrix<ModuloInteger> A = DenseMatrix.valueOf(new ModuloInteger[][]
            {{ new ModuloInteger(1), new ModuloInteger(2)},
             { new ModuloInteger(3), new ModuloInteger(4)}});

        DenseMatrix<ModuloInteger> b = DenseMatrix.valueOf(new ModuloInteger[][]
            {{ new ModuloInteger(9)},
             { new ModuloInteger(9)}});

        LUDecomposition lu = LUDecomposition.valueOf(A);
        
        System.out.println("This next matrix should be the solution to x1 + 2x2 = 9" +
                           " and 3x1 + 4x2 = 9 , which is x1=2, x2=9");
        DenseMatrix<ModuloInteger> solved = lu.solve(b);
        
        solved.toText().println();

        DenseMatrix<ModuloInteger> T = DenseMatrix.valueOf(new ModuloInteger[][]
            {{ new ModuloInteger(1), new ModuloInteger(1), new ModuloInteger(1)},
             { new ModuloInteger(4), new ModuloInteger(2), new ModuloInteger(1)},
             { new ModuloInteger(9), new ModuloInteger(3), new ModuloInteger(1)}});

        DenseMatrix<ModuloInteger> S = DenseMatrix.valueOf(new ModuloInteger[][]
            {{ new ModuloInteger(3)},
             { new ModuloInteger(9)},
             { new ModuloInteger(6)}});

        LUDecomposition lud = LUDecomposition.valueOf(T);
        DenseMatrix<ModuloInteger> testSolve = lud.solve(S);

        System.out.println("THIS SHOULD YIELD SOMETHING::: ");
        testSolve.toText().println();


        System.out.println("========== Testing Polynomials and polynomial division: ==========");
        
        Zn zn = new Zn(Utilities.F_big);

        BigInteger[] c1 = new BigInteger[3];
        c1[0] = BigInteger.valueOf(3); c1[1] = BigInteger.valueOf(4);
        c1[2] = BigInteger.valueOf(2);

        BigInteger[] c2 = new BigInteger[2];
        c2[0] = BigInteger.valueOf(1); c2[1] = BigInteger.valueOf(8);
        
        Polynomial p1 = new Polynomial(zn, "x", c1);
        Polynomial p2 = new Polynomial(zn, "x", c2);
        Polynomial p3 = p1.mul(p2);
        System.out.println("");
        System.out.println(p1.toString());
        System.out.println("");
        System.out.println(p2.toString());
        System.out.println("");
        System.out.println(p3.toString());
        System.out.println("");

        Polynomial div = p3.div(p1);
        System.out.println(div.toString());
        System.out.println("");

        System.out.println("========== DONE TESTING matrix and poly ===========");

        /**
         * Testing done
         */

        //reset the utility class
        int n_old = Utilities.n;
        int t_old = Utilities.t;
        BigInteger F_big_old = Utilities.F_big;
        int F_old = Utilities.F;
        Zn zn_old = Utilities.zn;
        System.out.println("SIZE OF FIELD: " + Utilities.F);

        boolean success = true;
        int NoOfPlayers = 0;
        int maxPlayers = 101;
        System.out.println("Testing if the program can handle secret sharing when" +
                           " dealing with up to "+maxPlayers+" players.");
        HIM M = new HIM();
        M.constructHIM();

        cc = new CircuitController();
        cc.start();

        TestController test = new TestController();

        Player testPlayer = new Player(99, M, test, cc, false);
        for(int i = 3; i <= maxPlayers; i++){
            Utilities.n = i;
            Utilities.t = i/3;
            Utilities.F_big = BigInteger.valueOf(i*2).nextProbablePrime();
            Utilities.F = Utilities.F_big.intValue();
            Utilities.zn = new Zn(Utilities.F_big);
            SharingProtocols ss1 = new SharingProtocols(new Random(0), testPlayer);
            BigInteger[] points = ss1.generateShares(Utilities.t);
            HashMap<Integer, BigInteger> testMap = new HashMap<Integer, BigInteger>();
            for(int j = 1 ; j<=points.length; j++){
                testMap.put(j,points[j-1]);
            }
            BigInteger secret = ss1.lagrange(testMap, Utilities.t, 0);
            int real_secret = ss1.getSecret();
            if(secret.intValue() != real_secret){
                success = false;
                NoOfPlayers = i;
                System.out.println("True secret was: " + real_secret +
                                   ", while the failed lagrange secret became: " + secret);
                break;
            }
            NoOfPlayers = i;
        } 
        if(success){
            System.out.println("Every test was successful!!");
        }
        else{
            System.out.println("Test failed when having " + NoOfPlayers + " players in the game.");
        }
        testPlayer.shutdown();

        // Testing GenerateTriples
        System.out.println("");
        System.out.println("================================================");
        System.out.println("Testing the generateTriples protocol with "+Utilities.n+" players:");
        Utilities.n = n_old;
        Utilities.t = t_old;
        Utilities.F_big = F_big_old;
        Utilities.F = F_old;
        Utilities.zn = zn_old;

        HashMap<Integer, Player> players = new HashMap<Integer, Player>();

        // Testing stuff - not gen trip

        Utilities.n_prime = 5;
        Utilities.t_prime = 1;
        Utilities.F_big = BigInteger.valueOf(17);
        Utilities.F = 17;
        HashMap<Integer, BigInteger> testMap= new HashMap<Integer, BigInteger>();
        testMap.put(1, BigInteger.valueOf(0));
        testMap.put(2, BigInteger.valueOf(1));
        testMap.put(3, BigInteger.valueOf(10));
        testMap.put(4, BigInteger.valueOf(10));
        testMap.put(5, BigInteger.valueOf(1));

        SharingProtocols testSha = new SharingProtocols(new Random(0), testPlayer);
        BigInteger sec = testSha.lagrange(testMap, 2, 0);
        sec = sec.mod(Utilities.F_big);
        System.out.println("================================================");
        System.out.println("TESTING RECOSNPRIV.. Secret was: " + sec);

        Polynomial poly = testSha.BW(2, testMap);
        System.out.println("TESTING BW.. Secret was: " + poly.getCoeff(0));

        testMap= new HashMap<Integer, BigInteger>();
        testMap.put(1, BigInteger.valueOf(9));
        testMap.put(2, BigInteger.valueOf(14));
        testMap.put(3, BigInteger.valueOf(16));
        testMap.put(4, BigInteger.valueOf(15));
        testMap.put(5, BigInteger.valueOf(11));

        sec = testSha.lagrange(testMap, 2, 0);
        sec = sec.mod(Utilities.F_big);
        System.out.println("================================================");
        System.out.println("TESTING RECOSNPRIV.. Secret was: " + sec);

        poly = testSha.BW(2, testMap);
        System.out.println("TESTING BW.. Secret was: " + poly.getCoeff(0));

        testMap = new HashMap<Integer, BigInteger>();
        testMap.put(1, BigInteger.valueOf(5));
        testMap.put(2, BigInteger.valueOf(1));
        testMap.put(3, BigInteger.valueOf(11));
        testMap.put(4, BigInteger.valueOf(1));
        testMap.put(5, BigInteger.valueOf(5));

        sec = testSha.lagrange(testMap, 2, 0);
        sec = sec.mod(Utilities.F_big);
        System.out.println("================================================");
        System.out.println("TESTING RECOSNPRIV.. Secret was: " + sec);

        poly = testSha.BW(2, testMap);
        System.out.println("TESTING BW.. Secret was: " + poly.getCoeff(0));


        System.out.println("================================================");
        System.exit(-1);

        // DONE...
        
        initUsers(Utilities.n);
        initPlayers(M, players, test, cc);
        for(Integer i: players.keySet()){
            players.get(i).generateTriples();            
        }        

    }

    public static void initUsers(int noOfUsers){
        User u;
        for(int i = 1; i<= noOfUsers; i++){
            //set up for random input. If we want something else, just
            //enter as param.
            //u = new User(i);
            //users.put(i, u);
        }
    }

    public static void initPlayers(HIM M, HashMap<Integer, Player> players, 
                                   TestController test, CircuitController cc){
        Player p;
        for(int i = 1; i <= Utilities.n ; i++){
            //Giving the players input as their ID.. 
            if(i > (Utilities.n-Utilities.t))
                p = new Player(i, M, test, cc, true);            
            else 
                p = new Player(i, M, test, cc, false);
            players.put(i,p);
        }
        HashMap<Integer, Integer> copyPlayers = new HashMap<Integer, Integer>();
        for(Integer i : players.keySet()){
            copyPlayers.put(i,i);
        }
        for(int i = 1; i <= Utilities.n ; i++){
            players.get(i).initPlayer(copyPlayers);
        }
        global_players = players;
        cc.setPlayers(players);
        cc.setUsers(users);
    }
    
    static int count =0, loops = 0, segment = 0;

    public void nextSegment(){
        count++;
        boolean next = false;
        if(count % Utilities.n_prime == 0){
            loops++;
            if(loops < Utilities.genTripAmount){ 
                for(Integer i: global_players.keySet()){
                    global_players.get(i).generateTriples();            
                }
            }
            else{
                next = true;
            }
        }
        if(next){
            System.out.println("Segment done - doing next segment");
            segment++;
            if(segment != Utilities.t){
                loops = 0;
                count = 0;
                for(Integer i: global_players.keySet()){
                    global_players.get(i).generateTriples();            
                }
            }
            else{
                //Okay - now let's try do some computation phase. There is
                //still no corruption present...
                
                cc.runCircuit();
            }
        }
    }

    public void returnHappyness(int sender, boolean happy){
        
    }

    public int getNoOfGenTrips(){
        return 0;
    }

    public void playerElimination(int sender, int p1, int p2){
        
    }

    public void time(boolean preparation){

    }
}